<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Force based label placement</title>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?2.6.0"></script>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.js?2.6.0"></script>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.geom.js?2.6.0"></script>
</head>
<body>
<script type="text/javascript" charset="utf-8">

    function httpGetAsync(theUrl, callback) {
        var xmlHttp = new XMLHttpRequest();

        xmlHttp.onreadystatechange = function () {
            if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
                callback(xmlHttp.responseText);
        };
        xmlHttp.open("GET", theUrl, true); // true for asynchronous
        const jwtToken = localStorage.getItem('jwt');
        xmlHttp.setRequestHeader('Authorization', `Bearer ${jwtToken}`);
        xmlHttp.send(null);
    }

    function getOrgLabel(peerName) {
        return peerName && peerName.substring(peerName.indexOf('.')+1, peerName.indexOf(':'));
    }

    function getPeerLabel(peerName) {
        return peerName ? peerName.split(':')[0].split(".")[0] : "Peer";
    }

    httpGetAsync("/channels/common/peers", function (res) {


        console.log(res);

        res= JSON.parse(res);

        if (!res || !res.length) return;

        var nodes = [];
        var labelAnchors = [];
        var links = [];
        var labelAnchorLinks=[];

        for (var orgIdx=0; orgIdx<res.length; orgIdx++) {
            const currentOrgName = getOrgLabel(res[orgIdx]);
            const orgNode = {label: currentOrgName, index: orgIdx, weight: 0.5};
            nodes.push(orgNode);
            labelAnchors.push({node: orgNode});
            labelAnchors.push({node: orgNode});
            labelAnchorLinks.push({source:orgIdx*2, target:orgIdx*2+1, weight:1});

            for (var restOrgIdx = 0; restOrgIdx < res.length; restOrgIdx++) {
                const orgName = getOrgLabel(res[restOrgIdx]);
                if (orgName !== currentOrgName) {
                    links.push({source: orgIdx, target: restOrgIdx, weight: 0.5})
                }
            }
        }


        for (var peerIdx=res.length; peerIdx<res.length*2; peerIdx++) {
            const currentPeerName = getPeerLabel(res[peerIdx-res.length]);
            const peerNode = {label: currentPeerName, index: peerIdx, weight: 0.5};
            nodes.push(peerNode);
            labelAnchors.push({node: peerNode});
            labelAnchors.push({node: peerNode});

            for (var restPeerIdx = res.length; restPeerIdx < res.length*2; restPeerIdx++) {
                links.push({source: restPeerIdx, target: restPeerIdx-res.length, weight: 0.5})
            }

            labelAnchorLinks.push({source:peerIdx*2, target:peerIdx*2+1, weight:1});
        }

        const orderer = {label:'ORDERER', index:res.length*2+1, weigth:1 };
        nodes.push(orderer);
        labelAnchors.push({node: orderer});
        labelAnchors.push({node: orderer});
        labelAnchorLinks.push({source:2*(res.length*2), target:2*(res.length*2)+1, weight:1});


        for (var orgIdx = 0; orgIdx < res.length; orgIdx++) {
             links.push({source: orgIdx, target: res.length*2, weight: 0.5})
        }


        var w = 960, h = 800;

        var labelDistance = 0;

        var vis = d3.select("body").append("svg:svg").attr("width", w).attr("height", h);

        var force = d3.layout.force().size([w, h]).nodes(nodes).links(links).gravity(1).linkDistance(150).charge(-16000).linkStrength(function (x) {
            return x.weight * 10
        });


        force.start();

        var force2 = d3.layout.force().nodes(labelAnchors).links(labelAnchorLinks).gravity(0).linkDistance(20).linkStrength(8).charge(-100).size([w, h]);
        force2.start();

        var link = vis.selectAll("line.link").data(links).enter().append("svg:line").attr("class", "link").style("stroke", "#CCC");

        var node = vis.selectAll("g.node").data(force.nodes()).enter().append("svg:g").attr("class", "node");
        node.append("svg:circle").attr("r", 10).style("fill", "#555").style("stroke", "#FFF").style("stroke-width", 3);
        node.call(force.drag);


        var anchorLink = vis.selectAll("line.anchorLink").data(labelAnchorLinks)//.enter().append("svg:line").attr("class", "anchorLink").style("stroke", "#999");

        var anchorNode = vis.selectAll("g.anchorNode").data(force2.nodes()).enter().append("svg:g").attr("class", "anchorNode");
        anchorNode.append("svg:circle").attr("r", 0).style("fill", "#FFF");
        anchorNode.append("svg:text").text(function (d, i) {
            return i % 2 == 0 ? "" : d.node.label
        }).style("fill", "#555").style("font-family", "Arial").style("font-size", 12);

        var updateLink = function () {
            this.attr("x1", function (d) {
                return d.source.x;
            }).attr("y1", function (d) {
                return d.source.y;
            }).attr("x2", function (d) {
                return d.target.x;
            }).attr("y2", function (d) {
                return d.target.y;
            });

        };

        var updateNode = function () {
            this.attr("transform", function (d) {
                return "translate(" + d.x + "," + d.y + ")";
            });

        };


        force.on("tick", function () {

            force2.start();

            node.call(updateNode);

            anchorNode.each(function (d, i) {
                if (i % 2 == 0) {
                    d.x = d.node.x;
                    d.y = d.node.y;
                } else {
                    var b = this.childNodes[1].getBBox();

                    var diffX = d.x - d.node.x;
                    var diffY = d.y - d.node.y;

                    var dist = Math.sqrt(diffX * diffX + diffY * diffY);

                    var shiftX = b.width * (diffX - dist) / (dist * 2);
                    shiftX = Math.max(-b.width, Math.min(0, shiftX));
                    var shiftY = 5;
                    this.childNodes[1].setAttribute("transform", "translate(" + shiftX + "," + shiftY + ")");
                }
            });


            anchorNode.call(updateNode);

            link.call(updateLink);
            anchorLink.call(updateLink);

        });
    });

</script>
</body>
</html>